package compiler;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.PrintStream;

import parse.Grm;
import parse.Lexer;
import absyn.Exp;
import errorMsg.ErrorMsg;
import findEscape.FindEscape;

public class TigerCompilerKernel {
	String libpath;

	public TigerCompilerKernel(String l) {
		libpath = l;
	}

	public void compile(FileInputStream inp, ErrorMsg errorMsg, String sourceFN) {
		StringBuffer postfix = new StringBuffer();
		StringBuffer codes = new StringBuffer();
		StringBuffer prefix = new StringBuffer();

		try {
//			Lexical Analysis Phase
			Lexer lexer = new Lexer(inp, errorMsg);

//			Parsing Phase
			Grm p = new Grm(lexer, errorMsg);
			java_cup.runtime.Symbol s = p.parse();

//			Semantic Analysis Phase
			semant.Semant sm = new semant.Semant(errorMsg);

			if (!errorMsg.anyErrors) {
				translate.Frag f;

//				Find Escaping Variables
				new FindEscape((Exp)s.value);

//				Translate Phase
				f = sm.transProgram((Exp)s.value);

				if (!errorMsg.anyErrors) {
					FileOutputStream oup = new FileOutputStream((sourceFN).replace(".tig", ".s"));
					FileOutputStream mid = new FileOutputStream((sourceFN).replace(".tig", ".ir"));
					PrintStream midp = new PrintStream(mid);
					while (f != null) {
						if (f instanceof translate.ProcFrag) {
							mips.Frame frm = (mips.Frame)(((translate.ProcFrag)f).frame);
							midp.println("\nPROCEDURE FRAGMENT : \n");
							tree.Print ptr = new tree.Print(midp);
							ptr.prStm(((translate.ProcFrag)f).body);

//							Canonicalization
							tree.StmList sl = canon.Canon.linearize(((translate.ProcFrag)f).body);
							canon.BasicBlocks bb = new canon.BasicBlocks(sl);
							canon.TraceSchedule ts = new canon.TraceSchedule(bb);

//							Code Generation Phase
							assem.InstrList il = ((mips.Frame)((translate.ProcFrag)f).frame).generateCode(ts.stms);

//							Register Allocation Phase
							regAlloc.RegAlloc ra = new regAlloc.RegAlloc(((translate.ProcFrag)f).frame, il);
							il = ra.rebuildInstr(frm, il);
							mips.Proc prc = frm.procEntryExit3(il);

							codes.append(prc.getGeneratedCode(ra));
						}
						else {

//							Dealing with Data Fragments
							translate.DataFrag df = (translate.DataFrag)f;
							postfix.append("\n\t.data\n");
							postfix.append(df.label + ":\n");
							postfix.append("\t.word\t" + df.str.length() + "\n");
							postfix.append("\t.ascii\t\"");
							char a[] = df.str.toCharArray();
							for (int i = 0; i < a.length; i++)
								switch (a[i]) {
								case '\n' : postfix.append("\\n");break;
								case '\t' : postfix.append("\\t");break;
								case '\\' : postfix.append("\\\\");break;
								case '\"' : postfix.append("\\\"");break;
								default : postfix.append(a[i]);
								}
							postfix.append("\"\n");
							postfix.append("\t.align\t2\n");
							postfix.append("\t.text\n");

						}
						f = f.next;
					}
					codes.append(postfix);
					prefix.append("\t.globl\t__start\n");
					prefix.append("__start:\n");
					prefix.append("\tj main\n");
					prefix.append(codes);

					byte[] buff = new byte[32768];
					FileInputStream lib = new FileInputStream(libpath);
					int n = lib.read(buff);
					for (int i = 0; i < n; i++)
						prefix.append((char)buff[i]);

					PrintStream ps = new PrintStream(oup);

					ps.print(prefix.toString());
				}
			}
			else {
//				Error handling
			}
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}
}
